/* 
 * File:   heart_beat_service.h
 * Author: james
 *
 * Created on 2011年4月21日, 下午7:24
 */

#ifndef HEART_BEAT_SERVICE_H
#define	HEART_BEAT_SERVICE_H

#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/thread.hpp>
#include <boost/scoped_ptr.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>

#include "log.h"
#include "element_manager.h"

namespace edm
{
    namespace common
    {

        //! defines the heart beat loop checking tick seconds.
#ifndef HEART_BEAT_TICKS
#define HEART_BEAT_TICKS 2
#endif

//!  description:  the heart beat service object
//!  thread safe:  unsafe
//!  Usage:     heart_beat_service_t<element_type> heart_beat_service();
//!             heart_beat_service.set_callback_function(callback_function);
//!             heart_beat_service.set_timedout(true, 25);
//!             heart_beat_service.set_max_limit(true, 3000000);
//!             heart_beat_service.start();
//!             heart_beat_service.async_add_element(element_type&);
template<typename element_type>
class heart_beat_service_t : private boost::noncopyable
{
public:
  //! the callback function define for element_type
  typedef boost::function<void(element_type&)> callback_function_type;

public:
  //!  heart_beat_service_t: constructor
  heart_beat_service_t();

  //!  heart_beat_service_t: destructor
  ~heart_beat_service_t();

  //!  set_callback_function: sets the timed out callback function
  //!  @in   callback_function_type: boost function object
  //!  @result: int
  int set_callback_function(callback_function_type callback_function);

  //!  set_timeout: sets the timed out flag and seconds for element ticks
  //!  @in   bool: timed out flag
  //!  @in   unsigned int: timed out seconds
  //!  @result: int
  int set_timeout(bool timeout_flag, unsigned int timedout);

  //!  set_max_limit: sets the max limit flag and number of element
  //!  @in   bool: element max limit flag
  //!  @in   unsigned long: max limit number
  //!  @result: int
  int set_max_limit(bool max_limit_flag, unsigned long max_limit);

  //!  start: start the service of heart beat
  //!  @result: int
  int start();

  //!  stop: stop the service of heart beat
  //!  @result: int
  int stop();

  //!  async_set_element: asynchronously sets the special element
  //!  @in   element_type&: deleted element reference
  //!  @result: void
  void async_set_element(element_type& element);

  //!  async_del_element: asynchronously deletes the special element
  //!  @in   element_type&: deleted element reference
  //!  @result: void
  void async_del_element(element_type& element);

private:
  //!  handle_timeout: handles all element when timer is timedout
  //!  @in   boost::system::error_code&: async wait error info
  //!  @result: int
  int handle_timeout();

  //!  set_element: sets the special element
  //!  @in   element_type&: setted element reference
  //!  @result: int
  int set_element(element_type& element);

  //!  del_element: deletes the special element
  //!  @in   element_type&: deleted element reference
  //!  @result: int
  int del_element(element_type& element);

private:
  //! io service object of boost for deadline time and thread working
  boost::asio::io_service m_io_service;

  //! io service work object of boost for keeping io service is running
  boost::asio::io_service::work m_io_work;

  //! identify whether service is running
  bool m_started;

  //! the service thread of heart beat service
  boost::scoped_ptr<boost::thread> m_thread;

  //! the timer for loop checking timedout task
  boost::asio::deadline_timer m_timer;

  //! mananges all element operation
  element_manager_t<element_type> m_element_manager;
};

template<typename element_type>
heart_beat_service_t<element_type>::heart_beat_service_t()
  : m_io_service(), m_io_work(m_io_service), m_started(false),
    m_thread(), m_timer(m_io_service)
{
  logtrace((COMMON, "heart_beat_service_t<element_type>::heart_beat_service_t()..."));
}

template<typename element_type>
heart_beat_service_t<element_type>::~heart_beat_service_t()
{
  logtrace((COMMON, "heart_beat_service_t<element_type>::~heart_beat_service_t() begin..."));

  stop();

  logtrace((COMMON, "heart_beat_service_t<element_type>::~heart_beat_service_t() end ok..."));
}

template<typename element_type>
int heart_beat_service_t<element_type>::set_callback_function(callback_function_type callback_function)
{
  logtrace((COMMON, "heart_beat_service_t<element_type>::set_callback_function() begin..."));

  if (m_started)
  {
    logwarn((COMMON, "heart_beat_service_t<element_type>::set_callback_function()::"
      "Heart beat service has been started and cannot set callback function again."));

    return -1;
  }

  if (m_element_manager.set_callback_function(callback_function))
  {
    logerror((COMMON, "heart_beat_service_t<element_type>::set_callback_function()::"
      "Failed to set callback function of element."));

    return -1;
  }

  logtrace((COMMON, "heart_beat_service_t<element_type>::set_callback_function() end ok..."));
  return 0;
}

template<typename element_type>
int heart_beat_service_t<element_type>::set_timeout(bool timeout_flag, unsigned int timedout)
{
  logtrace((COMMON, "heart_beat_service_t<element_type>::set_timedout() begin..."));

  if (m_started)
  {
    logwarn((COMMON, "heart_beat_service_t<element_type>::set_timeout()::"
      "Heart beat service has been started and cannot set timeout again."));

    return -1;
  }

  if (m_element_manager.set_timeout(timeout_flag, timedout))
  {
    logerror((COMMON, "heart_beat_service_t<element_type>::set_timeout()::"
      "Failed to set timeout flag and seconds of element."));

    return -1;
  }

  logtrace((COMMON, "heart_beat_service_t<element_type>::set_timeout() end ok..."));
  return 0;
}

template<typename element_type>
int heart_beat_service_t<element_type>::set_max_limit(bool max_limit_flag, unsigned long max_limit)
{
  logtrace((COMMON, "heart_beat_service_t<element_type>::set_max_limit() begin..."));

  if (m_started)
  {
    logwarn((COMMON, "heart_beat_service_t<element_type>::set_max_limit()::"
      "Heart beat service has been started and cannot set max limit of element agagin."));

    return -1;
  }

  if (m_element_manager.set_max_limit(max_limit_flag, max_limit))
  {
    logerror((COMMON, "heart_beat_service_t<element_type>::set_max_limit()::"
      "Failed to set max limit flag and number of element."));

    return -1;
  }

  logtrace((COMMON, "heart_beat_service_t<element_type>::set_max_limit() end ok..."));
  return 0;
}

template<typename element_type>
int heart_beat_service_t<element_type>::start()
{
  logtrace((COMMON, "heart_beat_service_t<element_type>::start() begin..."));

  if (m_started)
  {
    logwarn((COMMON, "heart_beat_service_t<element_type>::start()::"
      "Heart beat service is already started."));

    return 0;
  }

  m_timer.expires_from_now(boost::posix_time::seconds(HEART_BEAT_TICKS));
  m_timer.async_wait(boost::bind(&heart_beat_service_t<element_type>::handle_timeout, this));

  m_thread.reset(new boost::thread(
    boost::bind(&boost::asio::io_service::run, &m_io_service)));
  m_started = true;

  logtrace((COMMON, "heart_beat_service_t<element_type>::start() end ok..."));
  return 0;
}

template<typename element_type>
int heart_beat_service_t<element_type>::stop()
{
  logtrace((COMMON, "heart_beat_service_t<element_type>::stop() begin..."));

  if (!m_started)
  {
    logwarn((COMMON, "heart_beat_service_t<element_type>::stop()::"
      "Heart beat service is already stoped."));

    return 0;
  }

  m_io_service.stop();
  m_thread->join();
  m_started = false;

  logtrace((COMMON, "heart_beat_service_t<element_type>::stop() end ok..."));
  return 0;
}

template<typename element_type>
void heart_beat_service_t<element_type>::async_set_element(element_type& element)
{
  logtrace((COMMON, "heart_beat_service_t<element_type>::async_set_element() begin..."));
  if (!m_started)
  {
    logwarn((COMMON, "heart_beat_service_t<element_type>::async_set_element()::"
      "Heart beat service has not been started."));

    return;
  }

  m_io_service.post(boost::bind(&heart_beat_service_t<element_type>::set_element,
    this, element));
  logtrace((COMMON, "heart_beat_service_t<element_type>::async_set_element() end ok..."));
}

template<typename element_type>
void heart_beat_service_t<element_type>::async_del_element(element_type& element)
{
  logtrace((COMMON, "heart_beat_service_t<element_type>::async_del_element() begin..."));
  if (!m_started)
  {
    logwarn((COMMON, "heart_beat_service_t<element_type>::async_del_element()::"
      "Heart beat service has not been started."));

    return;
  }

  m_io_service.post(boost::bind(&heart_beat_service_t<element_type>::del_element,
    this, element));
  logtrace((COMMON, "heart_beat_service_t<element_type>::async_del_element() end ok..."));
}

template<typename element_type>
int heart_beat_service_t<element_type>::handle_timeout()
{
  logtrace((COMMON, "heart_beat_service_t<element_type>::handle_timeout() begin..."));

  m_timer.expires_at(m_timer.expires_at() + boost::posix_time::seconds(HEART_BEAT_TICKS));
  m_timer.async_wait(boost::bind(&heart_beat_service_t<element_type>::handle_timeout, this));

  if (m_element_manager.handle_timeout())
  {
    logerror((COMMON, "heart_beat_service_t<element_type>::handle_timeout()::"
      "Failed to invoke handle element at element manager."));

    return -1;
  }

  logtrace((COMMON, "heart_beat_service_t<element_type>::handle_timeout() end ok..."));
  return 0;
}

template<typename element_type>
int heart_beat_service_t<element_type>::set_element(element_type& element)
{
  logtrace((COMMON, "heart_beat_service_t<element_type>::set_element() begin..."));

  if (m_element_manager.set_element(element))
  {
    logerror((COMMON, "heart_beat_service_t<element_type>::set_element()::"
      "Failed to invoke set element at element manager."));

    return -1;
  }

  logtrace((COMMON, "heart_beat_service_t<element_type>::set_element() end ok..."));
  return 0;
}

template<typename element_type>
int heart_beat_service_t<element_type>::del_element(element_type& element)
{
  logtrace((COMMON, "heart_beat_service_t<element_type>::del_element() begin..."));

  if (m_element_manager.del_element(element))
  {
    logerror((COMMON, "heart_beat_service_t<element_type>::del_element()::"
      "Failed to invoke delete element at element manager."));

    return -1;
  }

  logtrace((COMMON, "heart_beat_service_t<element_type>::del_element() end ok..."));
  return 0;
}
        
    }
}

#endif
